<?php
class RestApi extends CApplicationComponent {
	public static function processRequest($action) {
		
		$request = new RestResponse();
		$data = RestUtils::initData();
		$result = null;
		
		if ($action == Constant::API_LOGIN) {
			RestAuth::login($data);
		}
		else if($action == Constant::API_LOGOUT) {
		    RestAuth::logout($data);
		}
		else if($action == Constant::API_INITIAL) {
		    RestUtils::setSuccess(
		        Constant::CODE_OK,
		        isset(Yii::app()->session['user']) ? array('user'=>Yii::app()->session['user']) : null,
		        $data
		    );
		}
		else if(RestAuth::authenticate($data)) {
			$request->setMethod(strtolower($_SERVER['REQUEST_METHOD']));
			
			if (!isset($_GET['model'])){
			    RestUtils::setError(Constant::ERR_MODEL_NOT_FOUND, $data);
			}
			elseif (!RestUtils::classExists($_GET['model'])) {
			    RestUtils::setError(Constant::ERR_MODEL_NOT_IMPLEMENTED, $data);
			}
			else {
			    try {
        			switch ($action) {
        				case Constant::API_LIST:
        				    RestUtils::setSuccess(Constant::CODE_OK, $_GET['model']::model()->findAll(), $data);
        					break;
        				case Constant::API_VIEW:
        				    RestUtils::setSuccess(Constant::CODE_OK, $_GET['model']::model()->findByPk($_GET['id']), $data);
        					break;
        				case Constant::API_SAVE:
        					//TODO: [HCN,2] chua thong nhat voi Tien ve api save model
        					break;
        				default:
        					RestUtils::setError(Constant::ERR_NOT_IMPLEMENTED, $data);
                        	exit;
        			}
			    }
			    catch (Exception $ex) {
			        RestUtils::setException($ex, $data);
			    }
			}
		}
		
		switch ($request->getHttpAccept()) {
			case 'json':
				RestApi::sendResponse(200, CJSON::encode($data), 'application/json');
				break;
			case 'xml':
				// using the XML_SERIALIZER Pear Package
				RestApi::sendResponse(200, RestUtils::convertArrayToXML($data), 'application/xml');
				break;
			default:
				RestApi::sendResponse(200, json_encode($data), 'application/json');
				break;
		}
		 
		return $request;
	}
	
	public static function sendResponse($status = 200, $body = '', $content_type = 'text/html')	{
		$status_header = 'HTTP/1.1 '.$status.' '.RestApi::getStatusCodeMessage($status);
		// set the status
		header($status_header);
		// set the content type
		header('Content-type: '.$content_type);
		 
		// pages with body are easy
		if(isset($body) && !empty($body)) {
			echo $body;
			exit;
		}
		// we need to create the body if none is passed
		else {
			// servers don't always have a signature turned on (this is an apache directive "ServerSignature On")
			$signature = ($_SERVER['SERVER_SIGNATURE'] == '') ? $_SERVER['SERVER_SOFTWARE'] . ' Server at ' . $_SERVER['SERVER_NAME'] . ' Port ' . $_SERVER['SERVER_PORT'] : $_SERVER['SERVER_SIGNATURE'];
			 
			// this should be templatized in a real-world solution
			$body = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
					<html>
						<head>
							<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
							<title>'.$status.' '.RestApi::getStatusCodeMessage($status).'</title>
						</head>
						<body>
							<h1>'.RestApi::getStatusCodeMessage($status).'</h1>
							<hr />
							<address>'.$signature.'</address>
						</body>
					</html>';
			 
			echo $body;
			exit;
		}
	}
	
	public static function getStatusCodeMessage($status) {
		$codes = parse_ini_file("Config.ini");
		return (isset($codes[$status])) ? $codes[$status] : '';
	}
}

class RestResponse {
	private $request_vars;
	private $data;
	private $http_accept;
	private $method;

	public function __construct() {
		$this->request_vars	= array();
		$this->data			= '';
		$this->http_accept	= (strpos($_SERVER['HTTP_ACCEPT'], 'json')) ? 'json' : 'xml';
		$this->method		= 'get';
	}

	public function setData($data) {
		$this->data = $data;
	}

	public function setMethod($method) {
		$this->method = $method;
	}

	public function setRequestVars($request_vars) {
		$this->request_vars = $request_vars;
	}

	public function getData() {
		return $this->data;
	}

	public function getMethod()	{
		return $this->method;
	}

	public function getHttpAccept()	{
		return $this->http_accept;
	}

	public function getRequestVars() {
		return $this->request_vars;
	}
}

class RestUtils {
	public static function initData() {
		return array (
			'response' => array(),
		    'navigation' => null,
			'result' => array (
			    'revision' => self::_getRevision()
			)
		);
	}
	
	public static function setWarning($code, $result, &$_data = array()) {
	    $config = parse_ini_file('Config.ini', true);
	    array_push($_data['response'], array ('type' => 'success', 'message' => $config['codes'][$code]));
	    $_data['result'] = $result;
	}
	
	public static function setError($code, &$_data = array()) {
		$config = parse_ini_file('Config.ini', true);
		array_push($_data['response'], array ('type' => 'error', 'message' => $config['codes'][$code]));
	}
	
	public static function setException($exception, &$_data = array()) {
		$config = parse_ini_file('Config.ini', true);
		array_push($_data['response'], array ('type' => 'error', 'message' => $exception->getMessage()));
	}
	
	public static function setSuccess($code, $result, &$_data = array()) {
		$config = parse_ini_file('Config.ini', true);
		array_push($_data['response'], array('type' => 'success', 'message' => $config['codes'][$code]));
		if (isset($result)) {
		    $_data['result'][key($result)] = $result[key($result)];
		}
	}
	
	public static function setInfo($code, $result, &$_data = array()) {
		$config = parse_ini_file('Config.ini', true);
		array_push($_data['response'], array ('type' => 'info', 'message' => $config['codes'][$code]));
		if (isset($result)) {
		    $_data['result'][key($result)] = $result[key($result)];
		}
	}
	
	public static function convertArrayToXML($object, $root = 'root') {
		header( "content-type: application/xml; charset=utf-8" );
		$xml = new DOMDocument( "1.0", "utf-8" );
		$parent = $xml->createElement($root);
		self::_appendChild($parent, $object, $xml);
		$xml->appendChild($parent);
		return $xml->saveXML();
	}
	
	public static function classExists($className) {
	    return file_exists(Yii::getPathOfAlias('application.models').DIRECTORY_SEPARATOR.$className.'.php');
	}
	
	private static function _appendChild(&$_parent, $_object, $_xml) {
		if((is_object($_object) || is_array($_object)) && count($_object) > 0) {
			foreach ($_object as $key => $value) {
				try {
					if((is_object($value) || is_array($value)) && count($value) > 0) {
						$child = $_xml->createElement(htmlentities($key, ENT_XML1));
						self::_appendChild($child, $value, $_xml);
					}
					else $child = $_xml->createElement(htmlentities($key, ENT_XML1), $value);
				}
				catch(Exception $e) {
					$child = $_xml->createElement('Error', 'Invalid Element');
				}
				$_parent->appendChild($child);
			}
		}
	}
	
	private function _getRevision() {
	    if (file_exists(dirname(__FILE__) . '/../../.svn/wc.db')) {
	
	        $db = new SQLite3(dirname(__FILE__) . '/../../.svn/wc.db');
	
	        $result = $db->query('SELECT changed_revision FROM nodes WHERE local_relpath = "" LIMIT 1');
	        if ($result) {
	            $row = $result->fetchArray();
	            if ($row['changed_revision'] != "") {
	                $row['changed_revision'] = $row['changed_revision'];
	            }
	        }
	        $svnrev = $row['changed_revision'];
	    } else if (file_exists(dirname(__FILE__) . '/../../.svn/entries')) {
	        $svn = File('.svn/entries');
	        $svnrev = $svn[10];
	        unset($svn);
	    }
	
	    return $svnrev;
	}
}

class RestAuth {
	public static function login(&$data = array()) {
	    $loginInfo = CJSON::decode(file_get_contents('php://input'), false);
		if (!isset($loginInfo->username) || !isset($loginInfo->password)) {
			RestUtils::setError(Constant::ERR_UNAUTHORIZED, $data);
		}
		else {
			try {
				$user = User::model()->find(
						'(username=:username OR email=:username)
						AND password=:password
						AND status=:status',
						array(
								':username'=>$loginInfo->username,
								':password'=>$loginInfo->password,
								':status'=>Constant::STATUS_ACTIVE
						)
				);
				if (count($user) > 0) {
					Yii::app()->session['user'] = $user;
					RestUtils::setSuccess(Constant::CODE_LOGIN_OK, array('user'=>$user), $data);
				}
				else {
					RestUtils::setError(Constant::ERR_UNAUTHORIZED, $data);
				}
			}
			catch (Exception $ex) {
			    RestUtils::setException($ex, $data);
			}
		}
	}
	
	public static function logout(&$data = array()) {
	    Yii::app()->session['user'] = null;
	    RestUtils::setSuccess(Constant::CODE_LOGOUT_OK, null, $data);
	}
	
	public static function authenticate(&$_data = array()) {
		if (isset(Yii::app()->session['user'])) {
			return true;
		}
		
		RestUtils::setError(Constant::ERR_UNAUTHORIZED, $_data);
		return false;
	}

	public static function http_digest_parse($txt)	{
		// protect against missing data
		$needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1);
		$data = array();
		$keys = implode('|', array_keys($needed_parts));

		preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@', $txt, $matches, PREG_SET_ORDER);

		foreach ($matches as $m) {
			$data[$m[1]] = $m[3] ? $m[3] : $m[4];
			unset($needed_parts[$m[1]]);
		}

		return $needed_parts ? false : $data;
	}

}